#include "ui.h"

/* A UI handler */

// Dependency on libsmacker
#include <Smacker/smacker.h>

// Send in the externs
extern GLuint cursor_list;
extern struct cfg_struct *cfg;
extern GLfloat aspect;
extern int mx, my;

extern Mix_Chunk *sfx_click;

int ui_main(struct s_ui_screen s_ui)
{
	// set to 0 to run.  set to non-zero to exit and change gamestate
	int done = 0,i;

	// full-screen fade to/from black silliness
	unsigned char fade_mode = 1;
	float fade_amount = 0.0;
	Uint32 fade_ticks;

	// Just a temp surface for holding the output from smk read, and other conversions
	SDL_Surface *frame = NULL;

	// On-screen Objects
	// Videos
	smk *s;
	unsigned long *w, *h;
	// Video frame textures
	GLuint *frame_tex;
	// Display list for videos
	GLuint smk_dl;
	// Limitation: all SMKs on screen run at the same interval.
	SDL_TimerID frame_timer = NULL;

	// Textures
	GLuint *ui_tex;
	unsigned int *tex_w, *tex_h;

	// Screen positioning (letterbox / pillarbox)
	GLuint xoffset, yoffset;
	float scale_x, scale_y, unscale_x, unscale_y;

	// Begin!
	/* Perform a conversion for scaled items to screen coords. */
	float ui_aspect = (s_ui.w) / (s_ui.h);
	if (ui_aspect > aspect) // Wide UI on narrow screen: match W, scale H
	{
		scale_x = s_ui.w;
		scale_y = s_ui.w / aspect;
		xoffset = 0;
		yoffset = (((s_ui.w / aspect) - s_ui.h) / 2) + .5;
	}
	else // 4:3 movie on wider screen: match H, scale W
	{
		scale_x = aspect * s_ui.h;
		scale_y = s_ui.h;
		xoffset = (((s_ui.h * aspect) - s_ui.w) / 2) + .5;
		yoffset = 0;
	}
	unscale_x = (float)atoi(cfg_get(cfg,"SCREEN_X"));
	unscale_y = (float)atoi(cfg_get(cfg,"SCREEN_Y"));

	// Initialize an smk *array
	s = malloc(s_ui.num_vid * sizeof(smk));
	// support arrays
	w = malloc(s_ui.num_vid * sizeof(unsigned long));
	h = malloc(s_ui.num_vid * sizeof(unsigned long));

	frame_tex = malloc (s_ui.num_vid * sizeof (GLuint));
	// Load all on-screen movies
	for (i = 0; i < s_ui.num_vid; i ++)
	{
		/* Go open the SMK files. */
		if (!(s[i] = smk_open_file(s_ui.vid[i].fname,SMK_MODE_MEMORY)))
		{
			/* Unable to load SMK file. Rats. */
			printf("WARNING: Unable to load %s.\n",s_ui.vid[i].fname);
			return GAMESTATE_ERROR;
		}

		smk_info_video(s[i],&w[i],&h[i],NULL);
		smk_enable_video(s[i],1);
		smk_first(s[i]);

		/* Set up video texture for this smk. */
		frame = tex_from_smkframe(smk_get_palette(s[i]),smk_get_video(s[i]),w[i],h[i]);
		if (frame == NULL)
		{
			fprintf(stderr,"WARNING: tex_from_smkframe returned NULL.\n");
			done = GAMESTATE_ERROR;
		} else {
			frame_tex[i] = build_texture (frame, GL_LINEAR);
			SDL_FreeSurface(frame);
		}
	}

	// Build all-movie display list
	reset_texture();
	smk_dl = glGenLists(1);
	glNewList(smk_dl, GL_COMPILE);
		for (i = 0; i < s_ui.num_vid; i ++)
		{
			use_texture(frame_tex[i]);
			glBox(xoffset+s_ui.vid[i].x,yoffset + s_ui.vid[i].y, w[i],h[i],0,0,powerOfTwo(w[i]),powerOfTwo(h[i]));
		}
	glEndList();

	// Load the other textures
	ui_tex = malloc (s_ui.num_tex * sizeof (GLuint));
	tex_w = malloc (s_ui.num_tex * sizeof (unsigned int));
	tex_h = malloc (s_ui.num_tex * sizeof (unsigned int));
	for (i = 0; i < s_ui.num_tex; i ++)
	{
		ui_tex[i] = load_texture_colorkey(s_ui.tex_fname[i],GL_LINEAR,0,255,255,&tex_w[i],&tex_h[i]);
	}

	/* UI Objects */
	GLuint obj_dl = glGenLists(s_ui.num_obj);
	GLuint obj_dl_hi = glGenLists(s_ui.num_obj);

	reset_texture();
	for (i = 0; i < s_ui.num_obj; i ++)
	{
		glNewList(obj_dl + i, GL_COMPILE);
			/* use_texture will expand to glBindTexture2d but only if it differs from the before
				for best performance sort UI elements by texture used */
			int j = s_ui.obj[i].tex;
			use_texture(ui_tex[j]);
			glBox(xoffset+s_ui.obj[i].x,yoffset+s_ui.obj[i].y,s_ui.obj[i].w,s_ui.obj[i].h,s_ui.obj[i].t_x,s_ui.obj[i].t_y,tex_w[j],tex_h[j]);
		glEndList();
	}

	reset_texture();
	for (i = 0; i < s_ui.num_obj; i ++)
	{
		if (s_ui.obj[i].reg_w)
		{
			glNewList(obj_dl_hi + i, GL_COMPILE);
				int j = s_ui.obj[i].tex;
				use_texture(ui_tex[j]);
				glBox(xoffset+s_ui.obj[i].x,yoffset+s_ui.obj[i].y,s_ui.obj[i].w,s_ui.obj[i].h,s_ui.obj[i].hi_x,s_ui.obj[i].hi_y,tex_w[j],tex_h[j]);
			glEndList();
		}
	}

	/* Strings */
	GLuint txt_dl = glGenLists(s_ui.num_txt);
	GLuint txt_dl_hi = glGenLists(s_ui.num_txt);
	reset_texture();
	for (i = 0; i < s_ui.num_obj; i ++)
	{
		glNewList(obj_dl + i, GL_COMPILE);
			/* use_texture will expand to glBindTexture2d but only if it differs from the before
				for best performance sort UI elements by texture used */
			int j = s_ui.obj[i].tex;
			use_texture(ui_tex[j]);
			glBox(xoffset+s_ui.obj[i].x,yoffset+s_ui.obj[i].y,s_ui.obj[i].w,s_ui.obj[i].h,s_ui.obj[i].t_x,s_ui.obj[i].t_y,tex_w[j],tex_h[j]);
		glEndList();
	}

	/* Load MUSIC */
	Mix_Music *ui_music = load_music(s_ui.name_bgm);
	if(!ui_music) {
		printf("music_load(\"%s\"): %s\n", s_ui.name_bgm, Mix_GetError());
	}

	// Highlight flags
	unsigned char *highlighted = malloc(s_ui.num_obj * sizeof(unsigned char));
	memset(highlighted,0,s_ui.num_obj * sizeof(unsigned char));

	// fade-to-black quad
	GLuint fade_list = glGenLists(1);
	glNewList(fade_list,GL_COMPILE);
		glDisable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
		glBegin(GL_QUADS);
			glVertex2i(0,0);
			glVertex2i(0,unscale_y);
			glVertex2i(unscale_x,unscale_y);
			glVertex2i(unscale_x,0);
		glEnd();
		glDisable(GL_BLEND);
		glEnable(GL_TEXTURE_2D);
		glColor3ub(255,255,255);
	glEndList();

	SDL_GetMouseState(&mx, &my);

	// Start the video timer
	if (s_ui.num_vid > 0)
	{
		double usf;
		smk_info_all(s[0],NULL,NULL,&usf);
		frame_timer = SDL_AddTimer((Uint32)((usf / 1000) + 0.5), next_frame, NULL);
	}

	if(Mix_PlayMusic(ui_music, -1)==-1) {
		printf("Mix_PlayMusic: %s\n", Mix_GetError());
	}


	SDL_Event event;

	unsigned char dirty = 1;

	fade_ticks = SDL_GetTicks() + 500;

	while (!done || fade_mode)
	{
	/* Check for events */
		while (SDL_PollEvent (&event))
		{
			if (!done && !fade_mode) {
			switch (event.type)
			{
				case SDL_USEREVENT:
					dirty = 1;
					for (i = 0; i < s_ui.num_vid; i ++)
					{
						if (smk_next(s[i]) == SMK_DONE)
						{
							// movie over, out of frames
							smk_first(s[i]);
						}

						frame = tex_from_smkframe(smk_get_palette(s[i]),smk_get_video(s[i]),w[i],h[i]);
						if (frame == NULL)
						{
							fprintf(stderr,"WARNING: tex_from_smkframe returned NULL.\n");
							done = GAMESTATE_ERROR;
						} else {
							glBindTexture(GL_TEXTURE_2D,frame_tex[i]);
							rebuild_texture (frame_tex[i], frame);
							SDL_FreeSurface(frame);
						}
					}
					break;
				case SDL_KEYUP:
					if (event.key.keysym.sym == SDLK_ESCAPE)
						done = s_ui.gamestate_escape;
					//                    done = next_gamestate;
					break;
				case SDL_MOUSEMOTION:
					// mouse move = cursor redraw
					dirty = 1;

					unsigned char hi_chg = 0;

					mx = event.motion.x;
					my = event.motion.y;

					// convert x / y to scale-coords
					int trans_x = (event.motion.x * scale_x / unscale_x) - xoffset;
					int trans_y = (event.motion.y * scale_y / unscale_y) - yoffset;

					for (i = 0; i < s_ui.num_obj; i ++)
					{
						unsigned char prev_hi = highlighted[i];
						if (trans_x > s_ui.obj[i].reg_x && trans_x < s_ui.obj[i].reg_x + s_ui.obj[i].reg_w &&
							trans_y > s_ui.obj[i].reg_y && trans_y < s_ui.obj[i].reg_y + s_ui.obj[i].reg_h)
							highlighted[i] = 1;
						else
							highlighted[i] = 0;
						if (!prev_hi && highlighted[i]) hi_chg = 1;
					}

					if (hi_chg)	Mix_PlayChannel(-1,sfx_click,0);

					break;
				case SDL_MOUSEBUTTONUP:
					for (i = 0; i < s_ui.num_obj; i ++)
					{
						if (highlighted[i] == 1) done = s_ui.obj[i].gamestate_click;
					}
					break;
				case SDL_QUIT:
					done = GAMESTATE_EXIT;
					break;
				default:
					break;
			}

			if (done != 0)
			{
				/* shutdown frame timer */
				if (frame_timer != NULL) SDL_RemoveTimer(frame_timer);
			
				fade_mode = 2;
				fade_ticks = SDL_GetTicks();
			}
			}
		}

		// fade-to-from-black
		if (fade_mode == 1)
		{
			fade_amount = ((float)fade_ticks - (float)SDL_GetTicks()) / 500;
			if (fade_amount <= 0)
			{
				fade_mode = 0; 
			}
			else dirty=1;
		} else if (fade_mode == 2) {
			fade_amount = ((float)SDL_GetTicks() - fade_ticks) / 500;
			if (fade_amount >= 1) fade_mode = 0; else dirty=1;
		}

		if (dirty) {
			/* Nuke everything */
			glClear(GL_COLOR_BUFFER_BIT);

			/* Put us into GLOrtho mode */
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			/* Match movie resolution */
			/* This is to draw "scaled" UI elements */
			glOrtho(0.0f,scale_x,scale_y,0.0f,-1.0f,1.0f);

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			if (!s_ui.vid_priority) glCallList(smk_dl);

			glEnable(GL_BLEND);
			for (i = 0; i < s_ui.num_obj; i ++)
			{
				if (highlighted[i])
					glCallList(obj_dl_hi + i);
				else
					glCallList(obj_dl + i);
			}
			glDisable(GL_BLEND);

			if (s_ui.vid_priority) glCallList(smk_dl);

			/* Flip back into GLOrtho mode */
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			/* Match screen resolution */
			/* This is to draw "unscaled" UI elements */
			glOrtho(0.0f,unscale_x,unscale_y,0.0f,-1.0f,1.0f);

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			glEnable(GL_ALPHA_TEST);
			glPushMatrix();
				glTranslatef(mx,my,0);
				glCallList(cursor_list);
			glPopMatrix();
			glDisable(GL_ALPHA_TEST);

			if (fade_mode)
			{
				glColor4f(0,0,0,fade_amount);
				glCallList(fade_list);
			}

			SDL_GL_SwapBuffers();

			dirty = 0;
		}

		/* Don't run too fast */
		SDL_Delay (1);
	}

	/* end music */
	//if (menu_click != NULL) { Mix_FreeChunk(menu_click); }
	if (ui_music != NULL) { Mix_HaltMusic(); Mix_FreeMusic(ui_music); }

	glDeleteLists(fade_list, 1);

	for (i = 0; i < s_ui.num_obj; i ++)
	{
		glDeleteLists(obj_dl,s_ui.num_obj);
		glDeleteLists(obj_dl_hi,s_ui.num_obj);
	}

	glDeleteLists(smk_dl, 1);

	glDeleteTextures( s_ui.num_tex, ui_tex);
	glDeleteTextures( s_ui.num_vid, frame_tex );

	for (i = 0; i < s_ui.num_vid; i ++)
		smk_close(s[i]);

	return done;
}
